/***************************************************************************
 *  Copyright: National ICT Australia,  2007 - 2011                        *
 *  Developed at the ATP lab, Networked Systems theme                      *
 *  Author(s): Athanassios Boulis, Yuriy Tselishchev                       *
 *  This file is distributed under the terms in the attached LICENSE file. *
 *  If you do not find this file, copies can be found by writing to:       *
 *                                                                         *
 *      NICTA, Locked Bag 9013, Alexandria, NSW 1435, Australia            *
 *      Attention:  License Inquiry.                                       *
 ***************************************************************************/

#include "TunableMACRL.h"
/***************RL******************/
#include "ResourceManager.h"
#include <ctime>
#include <vector>
/***********************************/
Define_Module(TunableMACRL);


// ********  Globals variables (one instance for all node)
stringstream traceString;
// ***********************************************


void TunableMACRL::startup()
{
	dutyCycle = par("dutyCycle");
	listenInterval = ((double)par("listenInterval")) / 1000.0;	// convert msecs to secs
	beaconIntervalFraction = par("beaconIntervalFraction");
	probTx = par("probTx");
	numTx = par("numTx");
	randomTxOffset = ((double)par("randomTxOffset")) / 1000.0;	// convert msecs to secs
	reTxInterval = ((double)par("reTxInterval")) / 1000.0;		// convert msecs to secs
	beaconFrameSize = par("beaconFrameSize");
	backoffType = par("backoffType");
	CSMApersistance = par("CSMApersistance");
	txAllPacketsInFreeChannel = par("txAllPacketsInFreeChannel");
	sleepDuringBackoff = par("sleepDuringBackoff");

	phyDataRate = par("phyDataRate");
	phyDelayForValidCS = (double)par("phyDelayForValidCS") / 1000.0;	// convert msecs to secs
	phyLayerOverhead = par("phyFrameOverhead");

	beaconTxTime = ((double)(beaconFrameSize + phyLayerOverhead)) * 8.0 / (1000.0 * phyDataRate);


/************************************** RL PARAMETERS *******************************************************/	
	activeLearning = par("activeLearning");
	startLearningTime = par("startLearningTime");

	frameLenght = par("frameLenght");
	frameWindow = par("frameWindow");
	slotNumber = par("slotNumber");
	soglia = par("soglia");
	alfa = par("alfa");
	beta = par("beta");
	gamma = par("gamma");
	delta = par("delta");
	epslon = par("epslon");
	lambda = par("lambda");
/***********************************************************************************************************/
	nodeRingLevel= -1;
	//~ hopToSink = -1;
	//~ nodeSynchronized = false;
	constTX = 0.0;
	oh = 0.0;
	ohPktSize = 0;
	dq = 0.0;
	ut = 0.0;
	lifeBattery = 0.0;
	contatoreSlot = 0;
	contatoreFrame = 0;
	frameCount = 0;
	EE = 0.0;
	idle = frameLenght;
	ESEE = -1;
/***********************************************************************************************************/
	firstFrame = true;
/***********************************************************************************************************/

	p.resize(slotNumber);
	probability.resize(slotNumber);
	radioState.resize(slotNumber);

	//~ totalSNnodes = getParentModule()->getParentModule()->getParentModule()->par("numNodes");
	//~ neighborTable.clear();

	for (unsigned int i=0; i<slotNumber; i++){
		radioState.at(i) = 1;
		probability.at(i) = 1;
		p.at(i)= 0;
	}
	
	//try to obtain the value of isSink parameter from application module (see .ini parameter --> SN.node[0].Application.isSink)
	if (getParentModule()->getParentModule()->findSubmodule("Application") != -1) {
		cModule *tmpApplication = getParentModule()->getParentModule()->getSubmodule("Application");
		isSink = tmpApplication->hasPar("isSink") ? tmpApplication->par("isSink") : false;
	} else {
		isSink = false;
	}
	
	if(!isSink){
		if(activeLearning)
			setTimer(START_LEARNING, startLearningTime);
		//~ trace() << "************* NODE '" << SELF_MAC_ADDRESS << "' is not SINK";
	}else{
		nodeRingLevel= 0;
		trace() << "************* NODE '" << SELF_MAC_ADDRESS << "' is SINK - NODE RING LEVEL: " << nodeRingLevel;
	}

/***********************************************************************************************************/

	switch (backoffType) {
		case 0:{
			if ((dutyCycle > 0.0) && (dutyCycle < 1.0))
				backoffType = BACKOFF_SLEEP_INT;
			else
				trace() << "Illegal value of parameter \"backoffType\" in omnetpp.ini.\n    Backoff timer = sleeping interval, but sleeping interval is not defined because duty cycle is zero, one, or invalid. Will use backOffBaseValue instead";
			backoffType = BACKOFF_CONSTANT;
			break;
		}
		case 1:{
			backoffType = BACKOFF_CONSTANT;
			break;
		}
		case 2:{
			backoffType = BACKOFF_MULTIPLYING;
			break;
		}
		case 3:{
			backoffType = BACKOFF_EXPONENTIAL;
			break;
		}
		default:{
			opp_error("\n[TunableMAC]:\n Illegal value of parameter \"backoffType\" in omnetpp.ini.");
			break;
		}
	}
	backoffBaseValue = ((double)par("backoffBaseValue")) / 1000.0;	// convert msecs to secs

	// start listening, and schedule a timer to sleep if needed
	toRadioLayer(createRadioCommand(SET_STATE, RX));
	if ((dutyCycle > 0.0) && (dutyCycle < 1.0)) {
		sleepInterval = listenInterval * ((1.0 - dutyCycle) / dutyCycle);
		setTimer(START_SLEEPING, listenInterval);
	} else {
		sleepInterval = -1.0;
	}

	macState = MAC_STATE_DEFAULT;
	numTxTries = 0;
	remainingBeaconsToTx = 0;
	backoffTimes = 0;

	srand(time(NULL));
	
	declareOutput("TunableMAC packet breakdown");
}


void TunableMACRL::timerFiredCallback(int timer)
{
	switch (timer) {

		case START_SLEEPING:{
			toRadioLayer(createRadioCommand(SET_STATE, SLEEP));
			setTimer(START_LISTENING, sleepInterval);
			break;
		}

		case START_LISTENING:{
			toRadioLayer(createRadioCommand(SET_STATE, RX));
			setTimer(START_SLEEPING, listenInterval);
			break;
		}

		case START_CARRIER_SENSING:{
			toRadioLayer(createRadioCommand(SET_STATE, RX));
			handleCarrierSenseResult(radioModule->isChannelClear());
			break;
		}

		case ATTEMPT_TX:{
			attemptTx();
			break;
		}

		case SEND_BEACONS_OR_DATA:{
			sendBeaconsOrData();
			break;
		}
		
/******************************************* TIMER RL***********************************************************************/
		//~ case SYN_FORWARD:{
			//~ TunableMacRLPacket *SYNPacket = new TunableMacRLPacket("TunableMac RL_SYN packet", MAC_LAYER_PACKET);
			//~ SYNPacket->setFrameType(RL_SYN);
			//~ SYNPacket->setSource(SELF_MAC_ADDRESS);
			//~ SYNPacket->setDestination(BROADCAST_MAC_ADDRESS);
			//~ SYNPacket->setHopToSink((hopToSink + 1));
			//~ nodeSynchronized = true;
			//~ toRadioLayer(SYNPacket);
			//~ trace() << "************* NODE '" << SELF_MAC_ADDRESS << "' sync with hopToSink: " << hopToSink << " forwards SYN(" << (hopToSink+1) <<")";
			//~ break;
		//~ }

		case START_LEARNING:{
			setTimer(FRAME_RL, 0);
			setTimer(SLOT_RL, 0);
			break;
		}
		
        case FRAME_RL:{
			if(contatoreFrame==frameWindow){
				
				ESEE = effectSetEnergyEfficient();
				double valP= 0;
				for(unsigned int i = 0; i < p.size(); i++){					
					if(p.at(i) == 1){
						valP= probability.at(i) + (lambda*ESEE*(1-probability.at(i)));
						
						if (valP <= soglia){
							if(radioState.at(i) == 1) p.at(i) = 1;
							else p.at(i) = 0;}
						else { 	if(radioState.at(i) == 1) p.at(i) = 0;
								else p.at(i) = 1;}
						
					}else{
						valP= probability.at(i) - (lambda*ESEE*probability.at(i));
						
						if (valP <= soglia){
							if(radioState.at(i) == 1) p.at(i) = 1;
							else p.at(i) = 0;}
						else { 	if(radioState.at(i) == 1) p.at(i) = 0;
								else p.at(i) = 1;}
					      
					}
					probability.at(i)= valP;
				}

				contatoreFrame= 0;
				framePrecEE.clear();
			}
			contatoreFrame++;
			idle = frameLenght;
			oh = 0.0;
			dq = 0.0;
			setTimer(FRAME_RL, frameLenght);
			
			if(contatoreFrame==1){
				traceString << "[" << SELF_MAC_ADDRESS << "]************** TIMER: FRAME_RL[" << contatoreFrame << "]   ESEE:[" << ESEE << "]   Probability:[ ";
				for(unsigned int i = 0; i < probability.size(); i++){
					traceString << probability.at(i) << "  ";
				}
				traceString << "]   P:[ ";
				for(unsigned int i = 0; i < p.size(); i++){
					traceString << p.at(i) << " ";
				}
				traceString << "]   RadioStates:[";
				for(unsigned int i = 0; i < radioState.size(); i++){
					traceString << radioState.at(i) << " ";
				}
				traceString<< "]";			
				trace() << traceString.str(); traceString.str("");
			}else{
				traceString << "[" << SELF_MAC_ADDRESS << "]************** TIMER: FRAME_RL[" << contatoreFrame << "]   RadioStates:[ ";
				for(unsigned int i = 0; i < radioState.size(); i++){
					traceString << radioState.at(i) << " ";
				}
				traceString<< "]";			
				trace() << traceString.str(); traceString.str("");
			}
			
			break;
        }

        case SLOT_RL:{
			if(contatoreSlot == slotNumber)
				contatoreSlot= 0;
			
			contatoreSlot++;
			
			traceString << "[" << SELF_MAC_ADDRESS << "]************* TIMER: SLOT_RL[" << contatoreSlot << "]   PktBufferSize:[" << buffer.size();

			if ((probability.at(contatoreSlot-1) <= soglia && contatoreSlot != slotNumber)){
				toRadioLayer(createRadioCommand(SET_STATE, SLEEP));
				radioState.at(contatoreSlot-1) = 0;
				traceString << "]   RadioState OFF";
			}else{
				toRadioLayer(createRadioCommand(SET_STATE, RX));
				radioState.at(contatoreSlot-1) = 1;
				traceString << "]   RadioState ON";
			}

			//~ if (radioState.at(contatoreSlot-1) == 1)
				//~ while (!buffer.empty()){
					//~ pktTime pt = buffer.front();
					//~ TunableMacRLPacket *Packet = pt.pktB;
					//~ dq += simTime().dbl() - pt.timestamp;
					//~ toRadioLayer(Packet); // attendere un delta t o comincare il prox pachetto immediatamente?
					//~ buffer.pop();                                    
				//~ }

			if(contatoreSlot == slotNumber){
				double c = (frameLenght/slotNumber)/3;
				double b = ((double)rand() * c) / (double)RAND_MAX;		
				traceString << " - Sending EE_Packet after " << b << " sec";
				EE = energyEfficient();
				frameCount = contatoreFrame;
				setTimer(EE_RL, b); //random tra 0 e lunghezza slot
			}
			trace() << traceString.str(); traceString.str("");

			setTimer(SLOT_RL, frameLenght/slotNumber);
			break;
		}

		case EE_RL:{
			traceString << "[" << SELF_MAC_ADDRESS << "]************* TIMER: EE_RL ";
				
//			Insert my value of EE for this frame into the vector of the map
			//~ neighborRecord newRec;
			//~ newRec.nodeID = SELF_MAC_ADDRESS;
			//~ newRec.EE = EE;
			//~ framePrecEE[frameCount].push_back(newRec);
			
			framePrecEE[frameCount].push_back(neighborRecord());
			framePrecEE[frameCount].at(framePrecEE[frameCount].size()-1).nodeID = SELF_MAC_ADDRESS;
			framePrecEE[frameCount].at(framePrecEE[frameCount].size()-1).EE = EE;
			
			TunableMacRLPacket *RLPacket = new TunableMacRLPacket("TunableMac RL packet", MAC_LAYER_PACKET);
			RLPacket->setFrameType(RL_PACKET);
			RLPacket->setEE(EE);
			RLPacket->setFrameCount(frameCount);
			RLPacket->setSource(SELF_MAC_ADDRESS);
			RLPacket->setDestination(BROADCAST_MAC_ADDRESS);
			//	RLPacket->setByteLength(RL_PKT_SIZE);
			toRadioLayer(RLPacket);

			traceString << " - Sending EE_Packet with EE:" << EE;
			traceString << " - PktBufferSize:" << buffer.size();

			//~ if (radioState.at(slotNumber-1) == 1)
				//~ while (!buffer.empty()){
					//~ pktTime pt = buffer.front();
					//~ RLPacket = pt.pktB;		
					//~ dq += simTime().dbl() - pt.timestamp; 
					//~ toRadioLayer(RLPacket);
					//~ buffer.pop();
				//~ }
				
			trace() << traceString.str(); traceString.str("");
			break;
		}


		default:{
			trace() << "WARNING: unknown timer callback " << timer;
		}
	}
}

void TunableMACRL::handleCarrierSenseResult(int returnCode)
{
	switch (returnCode) {

		case CLEAR:{
			/* For non-persistent and 1-persistent CSMA we just
			 * start the transmission. But for p-persistent CSMA
			 * we have to draw a random number and if this is
			 * bigger than p (=CSMApersistence) then we do not
			 * transmit but instead we carrier sense yet again.
			 */
			if (CSMApersistance > 0 && CSMApersistance < genk_dblrand(1)){
				setTimer(START_CARRIER_SENSING, phyDelayForValidCS);
				break;
			}

			/* We transmit: Reset the backoff counter, then
			 * proceed to calculate the number of beacons required to be
			 * sent based on sleep interval and beacon interval fraction
			 */
			backoffTimes = 0;
			if ((dutyCycle > 0.0) && (dutyCycle < 1.0)) {
				remainingBeaconsToTx = (int)ceil(sleepInterval * beaconIntervalFraction / beaconTxTime);
			} else {
				remainingBeaconsToTx = 0;
			}

			macState = MAC_STATE_TX;
			trace() << "Channel Clear, MAC_STATE_TX, sending " << remainingBeaconsToTx << " beacons followed by data";

			sendBeaconsOrData();
			break;

		}

		case BUSY:{
			/* For p-persistent and 1-persistent CSMA we carrier
			 * sense at a very fast rate until we find the channel
			 * free. Ideally we would get notified by the radio
			 * when the channel becomes free. Since most radios
			 * do not provide this capability, we have to poll.
			 * Because of polling and random start times for each
			 * node, 1-persistent does not necessarily cause colissions
			 * when 2 or more nodes are waiting to TX. Its performance
			 * can thus be better than p-persistent even with high
			 * traffic and and high number of contending nodes.
			 */
			if (CSMApersistance > 0) {
				setTimer(START_CARRIER_SENSING, phyDelayForValidCS);
				trace() << "Channel busy, persistent mode: checking again in " << phyDelayForValidCS << " secs";
				break;
			}

			/* For non-persistent CSMA, we simply calculate a back-off
			 * random interval based on the chosen back-off mechanism
			 */
			double backoffTimer = 0;
			backoffTimes++;

			switch (backoffType) {
				case BACKOFF_SLEEP_INT:{
					backoffTimer = (sleepInterval < 0) ? backoffBaseValue : sleepInterval;
					break;
				}

				case BACKOFF_CONSTANT:{
					backoffTimer = backoffBaseValue;
					break;
				}

				case BACKOFF_MULTIPLYING:{
					backoffTimer = (double)(backoffBaseValue * backoffTimes);
					break;
				}

				case BACKOFF_EXPONENTIAL:{
					backoffTimer = backoffBaseValue *
							pow(2.0, (double)(((backoffTimes - 1) < 0) ? 0 : backoffTimes - 1));
					break;
				}
			}

			backoffTimer = genk_dblrand(1) * backoffTimer;
			setTimer(START_CARRIER_SENSING, backoffTimer);
			trace() << "Channel busy, backing off for " << backoffTimer << " secs";

			/* If having a dutyCycle or relevant parameter is enabled
			 * go directly to sleep. One could say "wait for listenInterval
			 * in the case we receive something". This is highly improbable
			 * though as most of the cases we start the process of carrier
			 * sensing from a sleep state (so most likely we have missed the
			 * start of the packet). In the case we are in listen mode, if
			 * someone else is transmitting then we would have entered
			 * MAC_STATE_RX and the carrier sense (and TX) would be postponed.
			 */
			if ((sleepDuringBackoff) || ((dutyCycle > 0.0) && (dutyCycle < 1.0)))
				toRadioLayer(createRadioCommand(SET_STATE, SLEEP));
			break;
		}

		case CS_NOT_VALID:
		case CS_NOT_VALID_YET:{
			setTimer(START_CARRIER_SENSING, phyDelayForValidCS);
			trace() << "CS not valid yet, trying again.";
			break;
		}
	}
}

void TunableMACRL::fromNetworkLayer(cPacket * netPkt, int destination)
{

	TunableMacRLPacket *macPkt = new TunableMacRLPacket("TunableMac data packet", MAC_LAYER_PACKET);
	// *first* encapsulate the packet the then set its dest and source fields.
	encapsulatePacket(macPkt, netPkt);
	macPkt->setSource(SELF_MAC_ADDRESS);
	macPkt->setDestination(destination);
	macPkt->setFrameType(DATA_FRAME);
	collectOutput("TunableMAC packet breakdown", "Received from App");

	if(contatoreSlot>0)
		trace() << "[" << SELF_MAC_ADDRESS << "]************* PKT from NETWORK - RadioState:" << radioState.at(contatoreSlot-1);
	else
		trace() << "[" << SELF_MAC_ADDRESS << "]************* PKT from NETWORK";



	/* We always try to buffer the packet first */
	if (bufferPacket(macPkt)){
		/* If the new packet is the only packet and we are in default state
		 * then we need to initiate transmission process for this packet.
		 * If there are more packets in the buffer, then transmission is
		 * already being handled
		 */
		
		// tenere traccia del tempo di attesa di ciascun pkt nel buffer "TXBuffer"
		pktTime t;
		t.pktB = macPkt;
		t.timestamp = simTime().dbl();
		buffer.push(t);
		 
		 
		//~ if (macState == MAC_STATE_DEFAULT && TXBuffer.size() == 1 && radioState.at(contatoreSlot-1) == 1) {
		if (macState == MAC_STATE_DEFAULT && TXBuffer.size() == 1) {
			/* First action to initiate new transmission is to deal with
			 * all scheduled timers. In particular we need to suspend
			 * duty cycle timers (if present) and attempt to transmit
			 * the new packet with a maximum number of retries left.
			 */
			 
			trace() << "[" << SELF_MAC_ADDRESS << "]************* Trasmitting PKT";
			 
			if ((dutyCycle > 0.0) && (dutyCycle < 1.0)) {
				cancelTimer(START_LISTENING);
				cancelTimer(START_SLEEPING);
			}
			numTxTries = numTx;
			attemptTx();
		}
		else {
			//~ trace() << "[" << SELF_MAC_ADDRESS << "]************* RADIO OFF --> Buffering PKT...";

		}


	} else {
		/* bufferPacket() failed, buffer is full
		 * FULL_BUFFER control msg sent by virtualMAC code
		 */
		collectOutput("TunableMAC packet breakdown", "Overflown");
		trace() << "WARNING Tunable MAC buffer overflow";
	}
}

void TunableMACRL::attemptTx()
{
	trace() << "[" << SELF_MAC_ADDRESS << "]************* attemptTx(), TXBuffer size: " << TXBuffer.size() << ", numTxTries: " << numTxTries;

	if (numTxTries <= 0) {
		/* We can enter attemptTx from many places, in some cases
		 * the buffer may be empty. If its not empty but we have
		 * 0 tries left, then the front message is deleted.
		 */
		if (TXBuffer.size() > 0) {
			cancelAndDelete(TXBuffer.front());
			TXBuffer.pop();
			updateWaitingTimeQueue();
		}

		/* Check the buffer again to see if a new packet has to be
		 * transmitted, otherwise change to default state and
		 * reestablish the sleeping schedule.
		 */
		if (TXBuffer.size() > 0) {
			numTxTries = numTx;
			attemptTx();
		} else {
			macState = MAC_STATE_DEFAULT;
			trace() << "MAC_STATE_DEFAULT, no more pkts to attemptTx";
			/* We have nothing left to transmit, need to resume
			 * sleep/listen pattern. Starting by going to sleep
			 * immediately (timer with 0 delay).
			 */
			if ((dutyCycle > 0.0) && (dutyCycle < 1.0))
				setTimer(START_SLEEPING, 0);
		}
		return;
	}

	macState = MAC_STATE_CONTENDING;

	if (genk_dblrand(0) < probTx) {
		// This transmission attempt will happen after random offset
		setTimer(START_CARRIER_SENSING, genk_dblrand(1) * randomTxOffset);
		trace() << "MAC_STATE_CONTENDING, attempt " << numTx - numTxTries +1 << "/" << numTx << " contending";
	} else {
		// Move on to the next attempt after reTxInterval
		setTimer(ATTEMPT_TX, reTxInterval);
		trace() << "MAC_STATE_CONTENDING, attempt " << numTx - numTxTries +1 << "/" << numTx << " skipped";
		numTxTries--;
	}
}

void TunableMACRL::sendBeaconsOrData()
{
	if (remainingBeaconsToTx > 0) {
		remainingBeaconsToTx--;

		TunableMacRLPacket *beaconFrame =
			new TunableMacRLPacket("TunableMac beacon packet", MAC_LAYER_PACKET);
		beaconFrame->setSource(SELF_MAC_ADDRESS);
		beaconFrame->setDestination(BROADCAST_MAC_ADDRESS);
		beaconFrame->setFrameType(BEACON_FRAME);
		beaconFrame->setByteLength(beaconFrameSize);
		toRadioLayer(beaconFrame);
		toRadioLayer(createRadioCommand(SET_STATE, TX));
		trace() << "Sending Beacon";
		collectOutput("TunableMAC packet breakdown", "sent beacons");

		/* Set timer to send next beacon (or data packet). Schedule
		 * the timer a little faster than it actually takes to TX a
		 * a beacon, because we want to TX beacons back to back and
		 * we have to account for clock drift.
		 */
		setTimer(SEND_BEACONS_OR_DATA, beaconTxTime * 0.98);

	} 
	
	else if(){
		
	}
	
	else {
		if (TXBuffer.empty()) {
			numTxTries = 0; // safeguarding
			attemptTx(); 	// calling will set the sleeping patterns again.
			return;
		}

		toRadioLayer(TXBuffer.front()->dup());
		toRadioLayer(createRadioCommand(SET_STATE, TX));

		// Record whether this was an original transmission or a retransmission
		if (numTxTries == numTx){
			trace() << "Sending data packet";
			collectOutput("TunableMAC packet breakdown", "sent data pkts");
		}
		else{
			trace() << "Sending copy of data packet";
			collectOutput("TunableMAC packet breakdown", "copies of sent data pkts");
		}

		double packetTxTime = ((double)(TXBuffer.front()->getByteLength() +
		      phyLayerOverhead)) * 8.0 / (1000.0 * phyDataRate);
		numTxTries--;

		/* If we have chosen to transmit all packets in our
		 * buffer now that we found the channel free,
		 * we schedule the transmission of the next packet
		 * (or a copy of the current one) once the current
		 * transmission is done. If all retransmissions are done,
		 * we also delete the packet from the buffer. No beacons
		 * or carrier sensing for these packets.
		 */
		if (txAllPacketsInFreeChannel){
			/* schedule for sendBeaconsOrData() to be called
			 * again, a little faster than it takes to TX the
			 * packet. We want to keep the radio buffer non-
			 * empty, and we have to account for the clock drift
			 */
			setTimer(SEND_BEACONS_OR_DATA, packetTxTime * 0.95);
			if (numTxTries <= 0){
				cancelAndDelete(TXBuffer.front());
				TXBuffer.pop();
				updateWaitingTimeQueue();
				/* Set the numTxTries. If no more packets left, it
				 * will be reset when sendBeaconsOrData() is called
				 */
				numTxTries = numTx;
			}
			return;
		}
		/* If we have chosen to carrier sense (and TX beacons) for
		 * every packet, then we've just sent a _copy_ of the packet
		 * from the front of the buffer. We now move on to either
		 * A) the next copy or B) the next packet if the current
		 * packet has no TX attempts remaining. In case A, we need
		 * to delay the attempt by reTxInterval. Otherwise, in case
		 * B, attempt to transmit the next packet as soon as the
		 * current transmission ends.
		 */
		if (numTxTries > 0) {
			setTimer(ATTEMPT_TX, packetTxTime + reTxInterval);
		} else {
			setTimer(ATTEMPT_TX, packetTxTime);
		}
	}
}

void TunableMACRL::fromRadioLayer(cPacket * pkt, double rssi, double lqi)
{
	TunableMacRLPacket *macFrame = dynamic_cast <TunableMacRLPacket*>(pkt);
	macFrame->getMacRadioInfoExchange().RSSI = rssi;
	macFrame->getMacRadioInfoExchange().LQI = lqi;
	
	//~ trace() << "[" << SELF_MAC_ADDRESS << "]************* PKT from RADIO ";
	
	if (macFrame == NULL){
		collectOutput("TunableMAC packet breakdown", "filtered, other MAC");
		return;
	}
	int destination = macFrame->getDestination();
	if (destination != SELF_MAC_ADDRESS && destination != BROADCAST_MAC_ADDRESS){
		//oh = oh + TX_TIME(macFrame->getByteLength());
		collectOutput("TunableMAC packet breakdown", "filtered, other dest");
		return;
	}


	traceString << "[" << SELF_MAC_ADDRESS << "]************* PKT from RADIO - Src:" << macFrame->getSource() << " - Dest:" << macFrame->getDestination() << " - PktType:" << macFrame->getFrameType();
	
	
	if(!isSink){
		if(macFrame->getFrameType() == RL_PACKET)
			traceString << " - Received EE: " << macFrame->getEE();
			
		/***************************** Calcolo parametri di EE ****************************************/
		/****************** IDLE ************************************/
			constTX = TX_TIME(macFrame->getByteLength());
			traceString << " - PktTransmissionTime:" << constTX;
			idle = idle - constTX.dbl();
			traceString << " - IdleTime:" << idle;
		/****************** OVER Hearing ****************************/
			//~ if(macFrame->getDestination()!= SELF_MAC_ADDRESS){
				//~ oh = oh + TX_TIME(macFrame->getByteLength());
				//~ traceString << " - OverHearing Changed:" << oh;
			//~ }else{
				//~ traceString << " - OverHearing Not Changed:" << oh;
			//~ }
			
			ohPktSize= macFrame->getByteLength();
			
		/**********************************************************************************************/
	}

	trace() << traceString.str(); traceString.str("");


	switch (macFrame->getFrameType()) {

		case BEACON_FRAME:{
			collectOutput("TunableMAC packet breakdown", "received beacons");
			if (macState == MAC_STATE_DEFAULT) {
				if ((dutyCycle > 0.0) && (dutyCycle < 1.0))
					cancelTimer(START_SLEEPING);
			} else if (macState == MAC_STATE_CONTENDING) {
				cancelTimer(START_CARRIER_SENSING);
				cancelTimer(ATTEMPT_TX);
			} else if (macState == MAC_STATE_TX) {
				/* We ignore the received beacon packet because we
				 * are in the process of sending our own data
				 */
				trace() << "ignoring beacon, we are in MAC_STATE_TX"; 
				collectOutput("TunableMAC packet breakdown", "received beacons, ignored");
				break;
			}
			macState = MAC_STATE_RX;
			trace() << "MAC_STATE_RX, received beacon";
			if ((dutyCycle > 0.0) && (dutyCycle < 1.0)) {
				setTimer(ATTEMPT_TX, sleepInterval);
			} else {
				trace() << "WARNING: received a beacon packet without duty cycle in place";
				/* This happens only when one node has duty cycle while
				 * another one does not. TunableMac was not designed for
				 * this case as more thought is required a possible
				 * solution could be to include duration information
				 * in the beacon itself here we will just wait for 0.5
				 * secs to complete the reception  before trying to
				 * transmit again.
				 */
				setTimer(ATTEMPT_TX, 0.5);
			}
			break;
		}

		case DATA_FRAME:{
			trace() << "Received new DATA Pkt from Radio...";
			cPacket *netPkt = decapsulatePacket(macFrame);
			toNetworkLayer(netPkt);
			collectOutput("TunableMAC packet breakdown", "received data pkts");
			if (macState == MAC_STATE_RX) {
				cancelTimer(ATTEMPT_TX);
				attemptTx();
			}
			break;
		}
		
		//~ case RL_SYN:{
			//~ trace() << "************* NODE '" << SELF_MAC_ADDRESS << "' received RL_SYN pkt with hopToSink: " << macFrame->getHopToSink();
			//~ if(!nodeSynchronized){
				//~ hopToSink = macFrame->getHopToSink();
				//~ nodeSynchronized= true;
				//~ srand(time(NULL));
				//~ double delay = (rand()%200)/1000; //in s
				//~ setTimer(SYN_FORWARD, delay);
			//~ }
			//~ break;
		//~ }
		
		case RL_PACKET:{
			if(!isSink){
				trace() << "[" << SELF_MAC_ADDRESS << "]*************    'updateNeighborTable' called";
				updateNeighborInfo(macFrame->getSource(), macFrame->getEE(), macFrame->getFrameCount());
			}
			break;
		}
		
		default:{
			trace() << "WARNING: Received packet type UNKNOWN " <<"type: "<< macFrame->getFrameType();
			collectOutput("TunableMAC packet breakdown", "unrecognized type");
		}
	}
}

int TunableMACRL::handleControlCommand(cMessage * msg)
{
	TunableMacRLControlCommand *cmd = check_and_cast <TunableMacRLControlCommand*>(msg);

	switch (cmd->getTunableMacCommandKind()) {

		case SET_DUTY_CYCLE:{

			bool hadDutyCycle = false;
			// check if there was already a non zero valid duty cycle
			if ((dutyCycle > 0.0) && (dutyCycle < 1.0))
				hadDutyCycle = true;

			dutyCycle = cmd->getParameter();

			/* If a valid duty cycle is defined, calculate sleepInterval and
			 * start the periodic sleep/listen cycle using a timer
			 */
			if ((dutyCycle > 0.0) && (dutyCycle < 1.0)) {
				sleepInterval = listenInterval * ((1.0 - dutyCycle) / dutyCycle);
				if (!hadDutyCycle)
					setTimer(START_SLEEPING, listenInterval);

			} else {
				sleepInterval = -1.0;
				/* We now have no duty cycle, but we had it prior to this command
				 * Therefore we cancel all duty cycle related messages
				 * and ensure that radio is not stuck in sleeping state
				 * Since radio can sleep only when mac is in default state,
				 * we only wake up the radio in default state also
				 */
				if (hadDutyCycle) {
					cancelTimer(START_SLEEPING);
					cancelTimer(START_LISTENING);
					if (macState == MAC_STATE_DEFAULT)
						toRadioLayer(createRadioCommand(SET_STATE, RX));
				}
			}
			break;
		}

		case SET_LISTEN_INTERVAL:{

			double tmpValue = cmd->getParameter() / 1000.0;
			if (tmpValue < 0.0)
				trace() << "WARNING: invalid listen interval value sent to TunableMac";
			else {
				listenInterval = tmpValue;
				if ((dutyCycle > 0.0) && (dutyCycle < 1.0))
					sleepInterval = listenInterval * ((1.0 - dutyCycle) / dutyCycle);
				else
					sleepInterval = -1.0;
			}
			break;
		}

		case SET_BEACON_INTERVAL_FRACTION:{

			double tmpValue = cmd->getParameter();
			if ((tmpValue < 0.0) || (tmpValue > 1.0))
				trace() << "WARNING: invalid Beacon Interval Fraction value sent to TunableMac";
			else
				beaconIntervalFraction = tmpValue;
			break;
		}

		case SET_PROB_TX:{

			double tmpValue = cmd->getParameter();
			if ((tmpValue < 0.0) || (tmpValue > 1.0))
				trace() << "WARNING: invalid ProbTX value sent to TunableMac";
			else
				probTx = tmpValue;
			break;
		}

		case SET_NUM_TX:{

			double tmpValue = cmd->getParameter();
			if (tmpValue < 0 || tmpValue - ceil(tmpValue) != 0)
				trace() << "WARNING: invalid NumTX value sent to TunableMac";
			else
				numTx = (int)tmpValue;
			break;
		}

		case SET_RANDOM_TX_OFFSET:{

			double tmpValue = cmd->getParameter() / 1000.0;
			if (tmpValue <= 0.0)
				trace() << "WARNING: invalid randomTxOffset value sent to TunableMac";
			else
				randomTxOffset = tmpValue;
			break;
		}

		case SET_RETX_INTERVAL:{

			double tmpValue = cmd->getParameter() / 1000.0;
			if (tmpValue <= 0.0)
				trace() << "WARNING: invalid reTxInterval value sent to TunableMac";
			else
				reTxInterval = tmpValue;
			break;
		}

		case SET_BACKOFF_TYPE:{

			double tmpValue = cmd->getParameter();

			if (tmpValue == 0.0) {
				if ((dutyCycle > 0.0) && (dutyCycle < 1.0))
					backoffType = BACKOFF_SLEEP_INT;
				else
					trace() << "WARNING: invalid backoffType value sent to TunableMac. Backoff timer = sleeping interval, but sleeping interval is not defined because duty cycle is zero, one, or invalid.";
			}

			else if (tmpValue == 1.0) {
				if (backoffBaseValue <= 0.0)
					trace() << "WARNING: unable to set backoffType. Parameter backoffBaseValue has conflicting value";
				else
					backoffType = BACKOFF_CONSTANT;
			}

			else if (tmpValue == 2.0) {
				if (backoffBaseValue <= 0.0)
					trace() << "WARNING: unable to set backoffType. Parameter backoffBaseValue has conflicting value";
				else
					backoffType = BACKOFF_MULTIPLYING;
			}

			else if (tmpValue == 3.0) {
				if (backoffBaseValue <= 0.0)
					trace() << "WARNING: unable to set backoffType. Parameter backoffBaseValue has conflicting value";
				else
					backoffType = BACKOFF_EXPONENTIAL;
			}

			else
				trace() << "WARNING: invalid backoffType value sent to TunableMac";

			break;
		}

		case SET_BACKOFF_BASE_VALUE:{

			double tmpValue = cmd->getParameter() / 1000.0;
			if (tmpValue < 0)
				trace() << "WARNING: invalid backoffBaseValue sent to TunableMac";
			else
				backoffBaseValue = tmpValue;
			break;
		}

		case SET_RING_LEVEL:{
						
			//~ double tmpValue = cmd->getParameter();
			//~ if(tmpValue == 1)
			//~ oh += TX_TIME(ohPktSize);
			nodeRingLevel= (int)cmd->getParameter();
			trace() << "[" << SELF_MAC_ADDRESS << "]************* NODE RING LEVEL: " << nodeRingLevel;
	
			break;
		 }
		 
		}

	delete cmd;
	return 1;
}






void TunableMACRL::updateWaitingTimeQueue(){
	if(buffer.size()>0){
		pktTime pt = buffer.front();
		//~ TunableMacRLPacket *Packet = pt.pktB;
		dq += simTime().dbl() - pt.timestamp;
		buffer.pop();
	}
}



/************** METODO PER L'AGGIORNAMENTO E CREAZIONE DELLA TABELLA DEI VICINI(EFFECT SET)    *************/
void TunableMACRL::updateNeighborInfo(unsigned int nodeID, float EEvalue, unsigned int frameCount)
{
	unsigned int i = 0; 
	int pos = -1;

	traceString << "[" << SELF_MAC_ADDRESS << "]************* Updating Neighbor Info al FRAME '" << frameCount << "' - NodeID:" << nodeID << " - EE:" << EEvalue;
	
	for (i = 0; i < framePrecEE[frameCount].size(); i++)
		if (framePrecEE[frameCount].at(i).nodeID == nodeID)
			pos = i;

	if (pos == -1) {
		traceString << " [Nuovo vicino]";
		//~ neighborRecord newRec;
		//~ newRec.nodeID = nodeID;
		//~ newRec.EE = EEvalue;
		//~ framePrecEE[frameCount].push_back(newRec);

		framePrecEE[frameCount].push_back(neighborRecord());
		framePrecEE[frameCount].at(framePrecEE[frameCount].size()-1).nodeID = nodeID;
		framePrecEE[frameCount].at(framePrecEE[frameCount].size()-1).EE = EEvalue;

		
		traceString << " - Size:" << framePrecEE[frameCount].size();
	}else{
		traceString << " [Vicino già esistente]";
		framePrecEE[frameCount].at(pos).EE= EEvalue;
	}
	trace() << traceString.str(); traceString.str("");
	
	
	traceString << "[" << SELF_MAC_ADDRESS << "]************* Dettagli Neighbors Info al FRAME '" << frameCount << "' - ";
	for (i = 0; i<framePrecEE[frameCount].size(); i++){
		traceString << "(NodeID:" << framePrecEE[frameCount].at(i).nodeID << ";EE:"  << framePrecEE[frameCount].at(i).EE << ")";
	}
	trace() << traceString.str(); traceString.str("");
}



/************* METODO PER IL CALCOLO DELL'ENERGY EFFICIENT dell'effect set***************/
double TunableMACRL::effectSetEnergyEfficient()
{
	double EEjf= 0;
	double a= 0;
	double ris= 0;
	traceString << "[" << SELF_MAC_ADDRESS << "]************* Calcolo ESEE  [";

	for( map<unsigned int, vector<neighborRecord> >::iterator iter = framePrecEE.begin(); iter != framePrecEE.end(); ++iter ) {
		vector<neighborRecord> tempVec = (*iter).second;
		EEjf= 0;
		// EEjf include, oltre che i vicini, anche il nodo stesso
		for (unsigned int j = 0; j < tempVec.size(); j++){
			EEjf += tempVec.at(j).EE;
			traceString << " " << tempVec.at(j).EE;
		}
		traceString << "]-->[" << (EEjf / tempVec.size()) << "]  [ ";
			
		a += EEjf / tempVec.size();
	}
	
	traceString << "]  a:[" << a << "]";
	
	ris= a/frameWindow;
	
	traceString << "]  ESEE:[" << ris << "]";	
	trace() << traceString.str(); traceString.str("");
	
	return ris;
}



float TunableMACRL::energyEfficient()
{
	ResourceManager *tmpResourceManager = check_and_cast<ResourceManager*>(getParentModule()->getParentModule()->getSubmodule("ResourceManager")); 
	double spentEnergy = tmpResourceManager->getSpentEnergy(); 
	double initialEnergy = tmpResourceManager->par("initialEnergy");
	lifeBattery = (initialEnergy - spentEnergy);
	
	float  energy= (alfa * (1 - idle/frameLenght)) + (beta * (1 - oh/frameLenght)) + (gamma * (1 - ut)) + (delta * (1 - dq/frameLenght)) + (epslon*(lifeBattery/initialEnergy));
	
	trace() << "[" << SELF_MAC_ADDRESS << "]************* Calcolo EE: " << energy << " - Parametri: idle:"<< idle << " oh:" << oh << " ut:" << ut << " dq:" << dq << " lifebattery:"<< lifeBattery << " (initialEnergy:" << initialEnergy << ")";
	
	return energy;
}
